package cn.csc.module.pay.service.drawcash;

import cn.csc.framework.common.exception.util.ServiceExceptionUtil;
import cn.csc.framework.common.pojo.PageResult;
import cn.csc.framework.jzb.core.JzbClient;
import cn.csc.framework.jzb.vo.JzbResponse;
import cn.csc.module.pay.controller.admin.drawcash.vo.PayDrawCashCreateReqVO;
import cn.csc.module.pay.controller.admin.drawcash.vo.PayDrawCashExportReqVO;
import cn.csc.module.pay.controller.admin.drawcash.vo.PayDrawCashPageReqVO;
import cn.csc.module.pay.controller.admin.drawcash.vo.PayDrawCashUpdateReqVO;
import cn.csc.module.pay.controller.admin.fund.vo.PayFundPageReqVO;
import cn.csc.module.pay.controller.app.jzb.vo.JzbDrawCashReqVO;
import cn.csc.module.pay.controller.app.jzb.vo.MembershipWithdrawCashReqVO;
import cn.csc.module.pay.controller.app.jzb.vo.RegisterBehaviorRecordInfoReqVO;
import cn.csc.module.pay.controller.app.jzb.vo.SigningReqVO;
import cn.csc.module.pay.convert.drawcash.PayDrawCashConvert;
import cn.csc.module.pay.dal.dataobject.bank.PayBankDO;
import cn.csc.module.pay.dal.dataobject.drawcash.PayDrawCashDO;
import cn.csc.module.pay.dal.dataobject.fund.PayFundDO;
import cn.csc.module.pay.dal.dataobject.funddetailsinfo.PayFundDetailsInfoDO;
import cn.csc.module.pay.dal.mysql.bank.PayBankMapper;
import cn.csc.module.pay.dal.mysql.drawcash.PayDrawCashMapper;
import cn.csc.module.pay.dal.mysql.fund.PayFundMapper;
import cn.csc.module.pay.dal.mysql.funddetailsinfo.PayFundDetailsInfoMapper;
import cn.csc.module.pay.enums.ErrorCodeConstants;
import cn.csc.module.pay.service.jzborder.JzbThirdService;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

import static cn.csc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csc.module.pay.enums.ErrorCodeConstants.DRAW_CASH_NOT_EXISTS;
import static cn.csc.module.pay.enums.ErrorCodeConstants.MEMBERSHIP_WITH_DRAW_CASH_HTTP_ERR;

/**
 * 提款申请记录 Service 实现类
 *
 * @author liuchuang
 */
@Slf4j
@Service
@Validated
public class PayDrawCashServiceImpl implements PayDrawCashService {

    @Resource
    private PayDrawCashMapper payDrawCashMapper;
    @Resource
    private PayFundMapper payFundMapper;
    @Resource
    private PayFundDetailsInfoMapper payFundDetailsInfoMapper;
    @Resource
    private JzbThirdService jzbThirdService;
    @Resource
    private PayBankMapper payBankMapper;

    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private JzbClient jzbClient;

    @Value("${csc.payRate.tl}")
    private BigDecimal tlPayRate;
    @Value("${csc.jzb.drawDays}")
    private Integer drawDays;

    @Override
    public BigInteger createDrawCash(PayDrawCashCreateReqVO createReqVO) {
        // 插入
        PayDrawCashDO drawCash = PayDrawCashConvert.INSTANCE.convert(createReqVO);
        payDrawCashMapper.insert(drawCash);
        // 返回
        return drawCash.getId();
    }

    @Override
    public void updateDrawCash(PayDrawCashUpdateReqVO updateReqVO) {
        // 校验存在
        validateDrawCashExists(updateReqVO.getId());
        // 更新
        PayDrawCashDO updateObj = PayDrawCashConvert.INSTANCE.convert(updateReqVO);
        payDrawCashMapper.updateById(updateObj);
    }

    @Override
    public void deleteDrawCash(BigInteger id) {
        // 校验存在
        validateDrawCashExists(id);
        // 删除
        payDrawCashMapper.deleteById(id);
    }

    private void validateDrawCashExists(BigInteger id) {
        if (payDrawCashMapper.selectById(id) == null) {
            throw exception(DRAW_CASH_NOT_EXISTS);
        }
    }

    @Override
    public PayDrawCashDO getDrawCash(BigInteger id) {
        return payDrawCashMapper.selectById(id);
    }

    @Override
    public List<PayDrawCashDO> getDrawCashList(Collection<BigInteger> ids) {
        return payDrawCashMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<PayDrawCashDO> getDrawCashPage(PayDrawCashPageReqVO pageReqVO) {
        return payDrawCashMapper.selectPage(pageReqVO);
    }

    @Override
    public List<PayDrawCashDO> getDrawCashList(PayDrawCashExportReqVO exportReqVO) {
        return payDrawCashMapper.selectList(exportReqVO);
    }

    /**
     * 立马提现
     * @param reqVO 见证宝提现请求参数
     * @return
     */
    @Override
    public JzbResponse doDrawCash(JzbDrawCashReqVO reqVO){
        // 查询绑定成功的卡
        PayBankDO payBankDO = payBankMapper.selectByAppUserId(reqVO);

        if (payBankDO == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CHECK_AMOUNT_WITH_BANK_JZB_ERR);
        }

        PayFundPageReqVO pvo = new PayFundPageReqVO();
        pvo.setMerchantId(reqVO.getMerchantId());
        pvo.setAppUserId(reqVO.getAppUserId());
        PayFundDO payFundDO = payFundMapper.selectDetail(pvo);

        if (payFundDO.getBalance().compareTo(new BigDecimal(reqVO.getAmount())) == -1) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.CHECK_AMOUNT_WITH_MUCH_JZB_ERR);
        }
        //提现
        String seq = jzbClient.getCnsmrSeqNo();
        PayDrawCashDO cashDO = new PayDrawCashDO();
        cashDO.setMerchantId(reqVO.getMerchantId());
        cashDO.setAppUserId(reqVO.getAppUserId());
        cashDO.setDrawType(payBankDO.getBindBankType());
        cashDO.setMemberGlobalId(payBankDO.getMemberGlobalId());
        cashDO.setAcctName(payBankDO.getMemberName());
        cashDO.setMemberGlobalType(payBankDO.getMemberGlobalType());
        cashDO.setMemberAcctNo(payBankDO.getMemberAcctNo());
        cashDO.setSerialNo(reqVO.getSerialNo());
        cashDO.setDrawNo(seq);
        cashDO.setJzbCode(payFundDO.getJzbCode());
        cashDO.setStatus(3);   //提现中
        cashDO.setAmount(new BigDecimal(reqVO.getAmount()));  // 提现金额

        // 提现接口传递的手续费, 手续费在分账时收取
        BigDecimal fee = BigDecimal.ZERO;//提现手续费
        // 2023-11-28: 提现金额不足5000元时每笔加收2元/笔手续费
        if (BigDecimal.valueOf(5000L).compareTo(cashDO.getAmount()) > 0){
            fee = fee.add(BigDecimal.valueOf(2L)).setScale(2,BigDecimal.ROUND_HALF_UP);
            cashDO.setDrawMsg("提现金额不足5000元加收2元手续费");
        }

        // 到账金额
        BigDecimal actualAmt = cashDO.getAmount().subtract(fee).setScale(2, BigDecimal.ROUND_HALF_UP);

        cashDO.setHandFee(fee);
        log.info("doDrawCash fee(提现手续费)={}, actualAmt={}", fee, actualAmt);

        // 记录提现明细信息
        PayFundDetailsInfoDO infoDO = new PayFundDetailsInfoDO();
        infoDO.setAppUserId(reqVO.getAppUserId());
        infoDO.setTradeAmount(cashDO.getAmount());
        infoDO.setTypeId(2);  // 1:分账 2:提现
        infoDO.setStatus(0);

        // 编程式事务
        transactionTemplate.execute((status -> {
            payDrawCashMapper.insert(cashDO);
            infoDO.setDataId(cashDO.getId());
            payFundDetailsInfoMapper.insert(infoDO);
            return Boolean.TRUE;
        }));

        MembershipWithdrawCashReqVO cashReqVO = new MembershipWithdrawCashReqVO();
        cashReqVO.setMemberGlobalType(payBankDO.getMemberGlobalType());
        cashReqVO.setSubAcctNo(payFundDO.getJzbCode());
        cashReqVO.setMemberGlobalId(payBankDO.getMemberGlobalId());
        cashReqVO.setTranNetMemberCode(payFundDO.getAppUserId());
        cashReqVO.setMemberName(payBankDO.getMemberName());
        cashReqVO.setTakeCashAcctNo(payBankDO.getMemberAcctNo());
        cashReqVO.setOutAmtAcctName(payBankDO.getMemberName());
        cashReqVO.setCcy("RMB");
        cashReqVO.setCashAmt(actualAmt.toPlainString());
        cashReqVO.setRemark("");
        cashReqVO.setReservedMsg(fee.toPlainString());
        cashReqVO.setWebSign("");

        JzbResponse response;
        try {
            // 会员提现
            response = jzbThirdService.membershipWithdrawCash(cashReqVO, seq);
        } catch (Exception e){
            response = new JzbResponse();
            response.setSuccess(false);
            response.setData((JSONObject) JSONObject.toJSON(MEMBERSHIP_WITH_DRAW_CASH_HTTP_ERR));
            log.info("[doDrawCash]response={}", response);
            return response;
        }

        log.info("[doDrawCash]response={}", response);
        JSONObject data = response.getData().getJSONObject("Data");
        boolean success = response.isSuccess();

        transactionTemplate.execute((status -> {
            if (success){
                // 见证系统流水号
                cashDO.setSerialSeq(data.getString("FrontSeqNo"));
                // 1:提款成功
                cashDO.setStatus(1);
                cashDO.setPayDate(LocalDateTimeUtil.now());
                // TransferFee：转账手续费, 未启用	, 固定返回0.00
                String reservedMsg = data.getString("ReservedMsg");
                log.info("[doDrawCash]reservedMsg={}", reservedMsg);

                payDrawCashMapper.updateById(cashDO);

                /**
                 *  可用余额 balance : 当前可提现余额（用户可见），减去了 手续费 和 活动分账
                 *  累计可提现总额 balanceTotal : 累计订单金额
                 *  累计到账子账户总额 arrivalTotal: 累计到达子账户的金额，提现时不满5000会收取手续费
                 *  冻结资金 freeze: 显示给用户看，订单金额
                 *  总额 amount: 冻结金额 + 可提现余额
                 *  扣除通联支付手续费的金额 amountCompute: 累计订单金额 - 支付渠道费用
                 */
                // 资金表balance(可用余额)、amount(总额)预先扣减，若调用失败则定时任务补偿
                PayFundDO payDO = new PayFundDO();
                payDO.setId(payFundDO.getId());
                // amount 和 balance相应减少
                payDO.setAmount(new BigDecimal(reqVO.getAmount()));
                payFundMapper.drawCashAmount(payDO);

                PayFundDetailsInfoDO detailsInfoDO = new PayFundDetailsInfoDO();
                detailsInfoDO.setAppUserId(cashDO.getAppUserId());
                // 提现
                detailsInfoDO.setTypeId(2);
                // 成功
                detailsInfoDO.setStatus(5);
                detailsInfoDO.setDataId(cashDO.getId());
                payFundDetailsInfoMapper.updateInfo(detailsInfoDO);
            }else {
                // 提现失败
                cashDO.setStatus(2);
                // 提现次数超过8次
                if ((cashDO.getTaskNum() + 1) >= 8)
                    cashDO.setStatus(10);//待人工处理
                cashDO.setDrawMsg(data.toJSONString());
                cashDO.setTaskNum(cashDO.getTaskNum() + 1);
                payDrawCashMapper.updateById(cashDO);

                payFundDetailsInfoMapper.updateStatusById(infoDO.getId(),6);
            }
            return Boolean.TRUE;
        }));
        return response;
    }

    /**
     *
     * @param status  状态(0:待提现 1:提款成功,2:提款失败,3:提现中或者新建,4:取消提款,5:退单)
     * @param day
     */
    @Override
    public Integer taskDrawCash(int status, String day) {
        PayDrawCashPageReqVO pageReqVO = new PayDrawCashPageReqVO();
        pageReqVO.setStatus(status);
        if (status == 0) {
            if(StringUtils.isNotBlank(day)){
                LocalDate localDate = LocalDate.parse(day, DateTimeFormatter.ISO_DATE);
                pageReqVO.setTaskTime(localDate);
            }else {
                LocalDate localDate = LocalDate.now();
                pageReqVO.setTaskTime(localDate);
            }
        }
        pageReqVO.setPageNo(1);
        pageReqVO.setPageSize(20);

        AtomicInteger count = new AtomicInteger(0);
        PageResult<PayDrawCashDO> pageResult = getDrawCashPage(pageReqVO);
        // 第一页
        Optional.ofNullable(pageResult.getList())
                .ifPresent(list -> list.forEach(
                        drawCashDO -> {
                            try {
                                if (drawCashDO.getTaskNum() < 8) {
                                    JzbResponse response = getDrawCashUpdate(drawCashDO);
                                    if (response.isSuccess()) {
                                        count.incrementAndGet();
                                    }
                                }
                            } catch (Exception e) {
                                log.error("----定时提现出错----{}", JSON.toJSONString(drawCashDO), e);
                            }
                        }
                ));

        int pages = 1;
        Long total = pageResult.getTotal();
        if (total.intValue() > pageReqVO.getPageSize()) {
            if ((total.intValue() % pageReqVO.getPageSize()) == 0) {
                pages = total.intValue() / pageReqVO.getPageSize();
            } else {
                pages = (total.intValue() / pageReqVO.getPageSize()) + 1;
            }
        }

        // 第二页 至 最后一页
        IntStream.rangeClosed(2, pages).forEach(p -> {
            // 设置页码
            pageReqVO.setPageNo(p);
            // 分页查询
            PageResult<PayDrawCashDO> result = getDrawCashPage(pageReqVO);
            Optional.ofNullable(result.getList())
                    .ifPresent(list -> list.forEach(
                            drawCashDO -> {
                                try {
                                    if (drawCashDO.getTaskNum() < 8) {
                                        JzbResponse response = getDrawCashUpdate(drawCashDO);
                                        if (response.isSuccess()) {
                                            count.incrementAndGet();
                                        }
                                    }
                                } catch (Exception e) {
                                    log.error("----定时提现出错----{}", JSON.toJSONString(drawCashDO), e);
                                }
                            }
                    ));
        });

        return count.get();
    }

    @Override
    public JzbResponse signAgreement(SigningReqVO reqVO) {
        PayFundDO payFundDO = payFundMapper.selectFund(reqVO.getMerchantId(), reqVO.getAppUserId());

        RegisterBehaviorRecordInfoReqVO infoReqVO = new RegisterBehaviorRecordInfoReqVO();
        infoReqVO.setFunctionFlag("1");
        infoReqVO.setSubAcctNo(payFundDO.getJzbCode());
        infoReqVO.setTranNetMemberCode(payFundDO.getAppUserId());
        // yyyyMMddHHmmss
        infoReqVO.setOpClickTime(LocalDateTimeUtil.format(LocalDateTimeUtil.now(), DatePattern.PURE_DATETIME_PATTERN));
        infoReqVO.setIpAddress(reqVO.getIp());
        infoReqVO.setMacAddress(reqVO.getMacAddress().replaceAll("-", ":"));
        // 目前为小程序   1-app 2-平台H5网页 3-公众号 4-小程序
        infoReqVO.setSignChannel("4");
        infoReqVO.setReservedMsgOne("");
        infoReqVO.setReservedMsgTwo("");

        JzbResponse response = jzbThirdService.registerBehaviorRecordInfo(infoReqVO);

        PayFundDO req = new PayFundDO();
        if (response.isSuccess()) {
            //获取返回过来的消息
            JSONObject data = response.getData().getJSONObject("Data");

            req.setId(payFundDO.getId());
            if ("000000".equals(data.get("TxnReturnCode").toString()))
                req.setSignStatus(1);
            else
                req.setSignStatus(2);

        } else {
            req.setSignStatus(2);
        }
        payFundMapper.updateById(req);
        return response;
    }

    /**
     * 处理提现失败的情况
     * @param drawCashDO 提现参数
     * @return
     */
    public JzbResponse getDrawCashUpdate(PayDrawCashDO drawCashDO) {
        // TODO 先使用KFEJZB6273查询上次提现是否成功

        PayFundDO payFundDO = payFundMapper.selectFund(drawCashDO.getMerchantId(), drawCashDO.getAppUserId());

        PayBankDO payBankDO = new PayBankDO();
        payBankDO.setMemberGlobalId(drawCashDO.getMemberGlobalId());
        payBankDO.setMemberAcctNo(drawCashDO.getMemberAcctNo());
        payBankDO.setMemberGlobalType(drawCashDO.getMemberGlobalType());
        payBankDO.setMemberAcctNo(drawCashDO.getMemberAcctNo());

        log.info("[getDrawCashUpdate]drawCashDO={}", drawCashDO);

        // 重新生成 交易流水号
        String seq = jzbClient.getCnsmrSeqNo();
        log.info("[getDrawCashUpdate]oldSeq={}, seq={}", drawCashDO.getDrawNo(), seq);
        drawCashDO.setDrawNo(seq);

        //修改成定时执行过
        PayDrawCashDO cashDO = new PayDrawCashDO();
        cashDO.setId(drawCashDO.getId());
        cashDO.setTaskStatus(2);
        cashDO.setDrawNo(seq);
        payDrawCashMapper.updateById(cashDO);

        MembershipWithdrawCashReqVO cashReqVO = new MembershipWithdrawCashReqVO();
        cashReqVO.setMemberGlobalType(payBankDO.getMemberGlobalType());
        cashReqVO.setSubAcctNo(payFundDO.getJzbCode());
        cashReqVO.setMemberGlobalId(payBankDO.getMemberGlobalId());
        cashReqVO.setTranNetMemberCode(payFundDO.getAppUserId());
        cashReqVO.setMemberName(payBankDO.getMemberName());
        cashReqVO.setTakeCashAcctNo(payBankDO.getMemberAcctNo());
        cashReqVO.setOutAmtAcctName(payBankDO.getMemberName());
        cashReqVO.setCcy("RMB");
        cashReqVO.setCashAmt(drawCashDO.getAmount().toPlainString());
        cashReqVO.setRemark("");
        cashReqVO.setReservedMsg(drawCashDO.getHandFee().toPlainString());
        cashReqVO.setWebSign("");

        // 会员提现
        JzbResponse jzb;
        try {
            // 会员提现
            jzb = jzbThirdService.membershipWithdrawCash(cashReqVO,seq);
        } catch (Exception e){
            jzb = new JzbResponse();
            jzb.setSuccess(false);
            return jzb;
        }
        log.info("[getDrawCashUpdate]response={}", jzb);

        //获取返回过来的消息
        JSONObject data = jzb.getData().getJSONObject("Data");
        boolean success = jzb.isSuccess();
        transactionTemplate.execute((status) -> {
            if (success) {
                data.put("seqNo", drawCashDO.getDrawNo());
                String frontSeqNo = data.getString("FrontSeqNo");
                // TransferFee：转账手续费, 未启用	, 固定返回0.00
                String reservedMsg = data.getString("ReservedMsg");
                log.info("[getDrawCashUpate]见证宝返回的提现手续费={}", reservedMsg);

                // 资金表balance(可用余额)、amount(总额)扣减
                PayFundDO payDO = new PayFundDO();
                payDO.setId(payFundDO.getId());
                // amount 和 balance相应减少
                payDO.setAmount(drawCashDO.getAmount());
                payFundMapper.drawCashAmount(payDO);

                cashDO.setSerialSeq(frontSeqNo);
                cashDO.setStatus(1);
                cashDO.setTaskStatus(1);
                cashDO.setPayDate(LocalDateTimeUtil.now());
                payDrawCashMapper.updateById(cashDO);

                PayFundDetailsInfoDO infoDO = new PayFundDetailsInfoDO();
                infoDO.setAppUserId(drawCashDO.getAppUserId());
                infoDO.setTypeId(2);
                infoDO.setStatus(5);
                infoDO.setDataId(cashDO.getId());
                payFundDetailsInfoMapper.updateInfo(infoDO);

            } else {
                // 提现失败
                cashDO.setStatus(2);
                if ((drawCashDO.getTaskNum() + 1) >= 8)
                    cashDO.setStatus(10);//待人工处理
                cashDO.setTaskStatus(1);
                cashDO.setDrawMsg(data.toJSONString());
                cashDO.setTaskNum(drawCashDO.getTaskNum() + 1);
                payDrawCashMapper.updateById(cashDO);
                payFundDetailsInfoMapper.updateStatusByDataId(cashDO.getId(),6);
                log.error("-------[提现第三方手动调用失败]------" + data.toJSONString());
            }

            return Boolean.TRUE;
        });

        return jzb;
    }

}
